import java.util.Arrays;

/**
* A player's Hand(filled with cards)
*
* @author Kamil Olejnik
*/

public class Hand {

    public static int HAND_SIZE = 4;
    private static int LACK_OF_CARD = -1;
    private int[] hand = new int[HAND_SIZE];
    static int i = 1;
   

/**
* Creates a new Hand for a player
* @param deck A deck with cards
* @param player A current player
* @throws Exception while player can't draw another card
*/
    Hand(Deck deck, int playerNumber) {

        for (int i = 0; i < HAND_SIZE; i++) {
            hand[i] = Hand.LACK_OF_CARD;
        }
        for (int i = 0; i < HAND_SIZE; i++) {
            try {
                drawCard(deck, playerNumber);
            }catch (Exception e) {
                System.out.println(e.getMessage());
                System.out.println("Something is wrong");
            }
        }
    }


    /**
    * 
    * @param hand A hand held by player
    * @return bestHand if collision exist
    * @return hand if collision didn't exist
    */

    public static String getReducedHandCardSet(int [] hand) {
        int[] reducedHand = getReducedHand(hand);
        String cardSet = "";
        for(int i = 0; i < hand.length; i++) {
            boolean elementInReducedHand = false;
            for(int j = 0; j < reducedHand.length; j++) {
                if(reducedHand[j] == hand[i])
                    elementInReducedHand = true;
            }
            if(elementInReducedHand){
                cardSet += "1";
            }
            else {
                cardSet += "0";
            }
        }
        return cardSet;
    }
    
    
    public static int[] getReducedHand(int[] hand) {
        //System.out.println(i++);
        int[][][] collisionMatrix = new int [hand.length][hand.length][];
        boolean collisionExist = false;
        for (int i = 0; i < hand.length; i++) {
            for (int j = 0; j < hand.length; j++) {
                if ((i != j) && (hand[i] / 13 == hand[j] / 13 || hand[i] % 13 == hand[j] % 13)) {
                    collisionExist = true;
                    int[] reducedHandCandidate = new int[hand.length - 1];
                    for (int k = 0; k < reducedHandCandidate.length; k++) {
                        if (k < i)
                            reducedHandCandidate[k] = hand[k];
                        else
                            reducedHandCandidate[k] = hand[k + 1];
                    }
                    collisionMatrix[i][j] = getReducedHand(reducedHandCandidate);
                }
                else collisionMatrix[i][j] = new int[]{-2, -2, -2, -2};
            }
        }
        if (collisionExist) {
            int[] bestHand = new int[]{-1,-1,-1,-1};
            for (int i = 0; i < hand.length; i++) {
                for (int j = 0; j < hand.length; j++) {
                    if(!Arrays.equals(collisionMatrix[i][j], new int[]{-2, -2, -2, -2}))
                        if(Arrays.equals(bestHand, new int[]{-1, -1, -1, -1}))
                            bestHand = collisionMatrix[i][j];
                        else {
                            bestHand = giveBetterHand(bestHand, collisionMatrix[i][j]);
                        }
                }
            }
            return bestHand;
        }
        else
            return hand;
    }
        /**
        * @param handOne an array with cards in HandOne
        * @param handTwo an array with cards in HandTwo
        * @return handOne if length of handOne is greater than handTwo
        * @return handTwo if length of handTwo is greater than handOne
        *  
        */
    public static int [] giveBetterHand (int [] handOne, int[] handTwo) { //if equal returns handOne
        if(handOne.length > handTwo.length)
            return handOne;
        if(handOne.length < handTwo.length)
            return handTwo;
        int [] cardsFromOne = new int [handOne.length];
        int [] cardsFromTwo = new int [handTwo.length];
        for(int i = 0; i < handOne.length; i++) {
            cardsFromOne[i] = handOne[i]%13;
            cardsFromTwo[i] = handTwo[i]%13;
        }
        Arrays.sort(cardsFromOne);
        Arrays.sort(cardsFromTwo);
        for(int i = handOne.length - 1; i >= 0; i--) {
            if(cardsFromOne[i] < cardsFromTwo[i])
                return handOne;
            if(cardsFromOne[i] > cardsFromTwo[i])
                return handTwo;
        }
        return handOne;
    }

    public static int giveWorstCard(int [] hand) {
        for(int i = 0; i < hand.length; i++) {
            hand[i] = hand[i] % 13; //want just Rank;
        }
        Arrays.sort(hand);
        return hand[hand.length-1];
    }
        /**
    * @param deck A deck with cards
    * @param playerNumber An index of a player
    */

    public void drawCard(Deck deck, int playerNumber){
        int i = 0;
        while (hand[i] != Hand.LACK_OF_CARD) {
            i++;
        }
        hand[i] = deck.giveCard(playerNumber);
    }
    /**
    * @param deck A deck with cards
    * @param playerNumber An index of a player
    */
    public void drawAllCards(Deck deck, int playerNumber){
        System.out.println(getString());
        for(int i=0; i<HAND_SIZE; i++){
            hand[i] = deck.giveCard(playerNumber);
        }
        System.out.println(getString());
    }


    /*
    cardSet is binary representation of cards player want to discard e.g.
    we have cards: A|B|C|D and want to discard B then cardSet := 0100(2) = 4
    if we want to discard A and C the cardSet will be 1001(2) = 9
     */
    /**
    * @param deck A deck of cards
    * @param cardSet A set of cards in hand
    * @return discardedCards by player
    */
    public int[] discardCards(Deck deck, int cardSet){
        int[] discardedCards = new int[Integer.bitCount(cardSet)]; //Number of "1" in cardSet binary representation equals length of the array
        System.out.println("discardedCards:" + discardedCards.length);
        
        for (int i = HAND_SIZE - 1; i >= 0; i--) {
            if (cardSet % 2 == 1) {
                for (int j = 0; j < discardedCards.length; j++) {
                    if (discardedCards[j] != 0)
                        discardedCards[j] = hand[i];
                }
                deck.returnCardToDeck(hand[i]);
                hand[i] = -1;
            }
            cardSet /= 2;
        }
        return discardedCards;
    }

/**
* @param deck Deck with cards
* Method which discardAllCards
*
*/
    public void discardAllCards(Deck deck){
        for (int i = HAND_SIZE - 1; i >= 0; i--) {
            if(hand[i]!=-1) {
                deck.returnCardToDeck(hand[i]);
                hand[i] = -1;
            }
        }
    }
    /**
    * @param deck Deck with cards
    * @param playerNumber An index of a player
    * @param cardSet A set of cards in hand
    * @throws Exception e
    *
    */
    public void changeCards(Deck deck, int playerNumber, int cardSet){
        int[] discardedCards = discardCards(deck, cardSet);
        for (int i = 0; i < discardedCards.length; i++) {
            try
            {
                this.drawCard(deck, playerNumber);
            }
            catch (Exception e)
            {
                e.getMessage();
            }
        }
    }
     /**
    * @return Hand with cards
    */
    public int[] getCards() {
        return hand;
    }
    public void setCards(int[] cards) {
        hand = cards;
    }
    /**
    * @return A string s
    */
    public String getString(){
        String s = "";
        for (int i=0; i<HAND_SIZE; i++){
            s = s +
                    getOneCard(i);
        }
        return s;
    }
    /**
     * return "00","01"...."09"..."51" 
     * @param index
     * @return
     */
    public String getOneCard(int index){
        if(hand[index]<10){
            return ("0" + Integer.toString(hand[index]));
        }
        else{
            return Integer.toString(hand[index]);
        }
    }
}       

